package com.tfleo.mealssenger;

import com.tfleo.mealssenger.R;
import com.tfleo.mealssenger.utility.DataFetcher;
import com.tfleo.mealssenger.utility.NetworkTools;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Point;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.Settings.Secure;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.Toast;
import android.widget.ViewSwitcher;
import android.widget.AdapterView.OnItemSelectedListener;

/**
 * Activity for adding new Food object to Mealssenger server.
 * 
 * @author Theodore Felix Leo
 */
public class AddPlaceActivity extends MapActivity {
	private MapView vMap;
	private ImageView vCurrentMarker;
	
	private EditText vNameEntry;
    private EditText vDescEntry;
    private EditText vAddrEntry;
    private Button vNextBtn;
    
    private ViewSwitcher vSwitcher;
    private Spinner vCategory;
	private Button vBack;
	private Button vSubmit;
    private CheckBox vBreakfast;
    private CheckBox vLunch;
    private CheckBox vDinner;
    private CheckBox vSupper;
	
    private Context mContext;
	private MapController mMapController;
	private List<Overlay> mMapOverlays;
	private String mProvider = LocationManager.GPS_PROVIDER;
    private LocationManager mLocationManager;
    
    private Location mMyLocation;
    private GeoPoint mMyPos;
    
    private String mName;
	private String mDescription;
	private String mAddress;
	private String mCategory;
    private String mLatitude;
    private String mLongitude;
	private int mTime = 0; //value of binary combination supper-dinner-lunch-breakfast, 
						   //e.g. 0110 = 6 means dinner & lunch only
	
	private String mAuth;
	private int mUserId;
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
	    setContentView(R.layout.add_place);
	    mContext = this;
	    mAuth = Secure.getString(this.getContentResolver(), Secure.ANDROID_ID); //for authentication
	    mUserId = getSharedPreferences(Main.USER_DATA, MODE_PRIVATE).getInt(Main.USER_ID, 0);
	    
	    vSwitcher = (ViewSwitcher) findViewById(R.id.switcher);
	    
	    vNameEntry = (EditText) findViewById(R.id.name);
	    vDescEntry = (EditText) findViewById(R.id.desc);
	    vAddrEntry = (EditText) findViewById(R.id.addr);
		vNextBtn = (Button) findViewById(R.id.next);

		vBreakfast = (CheckBox) findViewById(R.id.bfast_check);
		vBreakfast.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				if (((CheckBox) v).isChecked()) {
					mTime += 1;
				} else {
					mTime -= 1;
				}
			}
		});

		vLunch = (CheckBox) findViewById(R.id.lunch_check);
		vLunch.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				if (((CheckBox) v).isChecked()) {
					mTime += 2;
				} else {
					mTime -= 2;
				}
			}
		});
		
		vDinner = (CheckBox) findViewById(R.id.dinner_check);
		vDinner.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				if (((CheckBox) v).isChecked()) {
					mTime += 4;
				} else {
					mTime -= 4;
				}
			}
		});
		
		vSupper = (CheckBox) findViewById(R.id.supper_check);
		vSupper.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				if (((CheckBox) v).isChecked()) {
					mTime += 8;
				} else {
					mTime -= 8;
				}
			}
		});
	
		vNextBtn.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				//Force to have name
				if((mName = vNameEntry.getText().toString()).trim().equals("")) {
					Toast.makeText(mContext, "Error: Please specify food name!", Toast.LENGTH_SHORT).show();
					return;
				}
				
				//Empty description is ok
				if((mDescription = vDescEntry.getText().toString()).trim().equals("")) {
					mDescription = "";
				}
				
				//Empty address is ok
				if((mAddress = vAddrEntry.getText().toString()).trim().equals("")) {
					mAddress = "";
				}
				
				vSwitcher.showNext();
			}
		});
	    
		if(!NetworkTools.isNetworkAvailable(mContext)) {
	    	Toast.makeText(getApplicationContext(),
					"Warning: Network is currently unavailable!", Toast.LENGTH_LONG).show();
	    }
		
	    mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); 
	    if(!mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
        	mProvider = LocationManager.NETWORK_PROVIDER;
        }
	    
        vMap = (MapView) findViewById(R.id.map);
        vMap.setBuiltInZoomControls(true);
        mMapController = vMap.getController();
        
        mMyLocation = mLocationManager.getLastKnownLocation(mProvider);
        showLocation(mMyLocation);
        
        vCategory = (Spinner) findViewById(R.id.category);
        ArrayAdapter<CharSequence> placeTypeAdapter = ArrayAdapter.createFromResource(
	            this, R.array.place_category_array, android.R.layout.simple_spinner_item);
	    placeTypeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
	    
	    vCategory.setAdapter(placeTypeAdapter);
	    vCategory.setOnItemSelectedListener(new OnItemSelectedListener() {
	    	public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
	            mCategory = parent.getItemAtPosition(pos).toString();
	            mTime = guessTime(mCategory);
	        }

	        public void onNothingSelected(AdapterView<?> parent) {
	        	mCategory = parent.getItemAtPosition(0).toString();
	        }
	        
	    });

		vBack = (Button) findViewById(R.id.back);
	    vBack.setOnClickListener(new OnClickListener() {
	    	public void onClick(View v) {
	    		vSwitcher.showPrevious();
	    	}
	    });
	    
	    vSubmit = (Button) findViewById(R.id.submit);
	    vSubmit.setOnClickListener(new OnClickListener() {
	    	public void onClick(View v) {
	    		if(mTime == 0) {
	    			Toast.makeText(mContext, "Error: Please specify this place's operating time!", 
	    					Toast.LENGTH_SHORT).show();
	    			return;
	    		}
	    		SubmitTask task = new SubmitTask();
	    		task.execute();
	    	}
	    });
        
	}
	
	/**
	 * Show marker to a location on map. This method is called once only
	 * during initialization. User's tap is handled by the MapOverlay object.
	 *  
	 * @param location Initial location, which is last known location of user.
	 */
	protected void showLocation(Location location) {
		mMyPos = new GeoPoint(
				(int) (location.getLatitude() * 1E6),
				(int) (location.getLongitude() * 1E6));
		
		MapView.LayoutParams lp = new MapView.LayoutParams(
				MapView.LayoutParams.WRAP_CONTENT,
				MapView.LayoutParams.WRAP_CONTENT,
				mMyPos,
				MapView.LayoutParams.CENTER);
		
		//Set new marker if it is not yet set
		if(vCurrentMarker != null) {
			vCurrentMarker.setLayoutParams(lp);
		} else {
			vCurrentMarker = new ImageView(this);
			vCurrentMarker.setImageResource(R.drawable.my_marker);
			vMap.addView(vCurrentMarker, lp);
		}
		
		mLatitude = Double.toString(mMyPos.getLatitudeE6()/1E6);
	    mLongitude = Double.toString(mMyPos.getLongitudeE6()/1E6);
		
	    setAddress(mMyPos);
		
		mMapController.animateTo(mMyPos);
		mMapController.setZoom(17);
		
		MapOverlay mapOverlay = new MapOverlay();
        mMapOverlays = vMap.getOverlays();
        mMapOverlays.clear();
        mMapOverlays.add(mapOverlay);
		vMap.invalidate();
	}


	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}
	
	/**
	 * Extension of Overlay class to handle tap on map and draw a marker
	 * on the tap location.
	 */
	class MapOverlay extends com.google.android.maps.Overlay
    {
        @Override
        public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when) {
            super.draw(canvas, mapView, shadow);                   
 
            //Translate the GeoPoint to screen pixels
            Point screenPts = new Point();
            mapView.getProjection().toPixels(mMyPos, screenPts);
 
            //Add marker
            Bitmap bmp = BitmapFactory.decodeResource(
                getResources(), R.drawable.place_marker);            
            canvas.drawBitmap(bmp, screenPts.x-25, screenPts.y-70, null);         
            return false;
        }
        
        @Override
        public boolean onTap(GeoPoint p, MapView mapView) {   
        	mMyPos = p;

        	mLatitude = Double.toString(mMyPos.getLatitudeE6()/1E6);
        	mLongitude = Double.toString(mMyPos.getLongitudeE6()/1E6);

        	setAddress(mMyPos);
        	
            return false;
        }        
    }
	
	/**
	 * Convert GeoPoint to Address and set the corresponding text view
	 * 
	 * @param p The location GeoPoint to be converted.
	 */
	private void setAddress(GeoPoint p) {
		Geocoder geocoder = new Geocoder(mContext, Locale.getDefault());
    	Address address;
    	String result = null;
    	List<Address> list = new ArrayList<Address>();
    	try {
    		list = geocoder.getFromLocation(p.getLatitudeE6()/1E6 , p.getLongitudeE6()/1E6 , 1);
    	} catch (IOException e) {
    		e.printStackTrace();
    	}
    	if(list.size() > 0) {
    		address = list.get(0);
    		result = address.getAddressLine(0); /*address.getLocality();*/
    		vAddrEntry.setText(result);
    	}                       
	}
	
	/**
	 * Add new place data to server
	 * 
	 * @param name Name of place
	 * @param description Description of place
	 * @param address Address of place
	 * @param category Category of place
	 * @param time Operating time of place
	 * @param longitude Longitude coordinate of place
	 * @param latitude Latitude coordinate of place
	 * 
	 * @return true if successful, false otherwise
	 */
	private boolean addPlace(String name, String description, String address, String category, int time, 
			String longitude, String latitude) {
		HttpClient httpclient = new DefaultHttpClient();
		HttpPost httppost = new HttpPost(Main.SERVER_URL + "place/add");
		
		try {
			List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
			nameValuePairs.add(new BasicNameValuePair("name", name));
			nameValuePairs.add(new BasicNameValuePair("description", description));
			nameValuePairs.add(new BasicNameValuePair("address", address));
			nameValuePairs.add(new BasicNameValuePair("category", category));
			nameValuePairs.add(new BasicNameValuePair("time", Integer.toString(time)));
			nameValuePairs.add(new BasicNameValuePair("longitude", longitude));
			nameValuePairs.add(new BasicNameValuePair("latitude", latitude));
			
			nameValuePairs.add(new BasicNameValuePair("me", Integer.toString(mUserId)));
			nameValuePairs.add(new BasicNameValuePair("auth", mAuth));
			httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

			//Execute HTTP Post Request
			ResponseHandler<String> responseHandler = new BasicResponseHandler();
			String responseBody = httpclient.execute(httppost, responseHandler);
			JSONObject response = new JSONObject(responseBody);
			
			if(response.getInt("code") == 1) {
				return true;
			} else {
				return false;
			}
		} catch(JSONException e){
			e.printStackTrace();
		} catch(IOException e) {
			e.printStackTrace();
		} catch(Exception e) {
			e.printStackTrace();
		}
		
		return false;
	}
	
	/**
	 * Task to handle submission of data to server.
	 * 
	 */
	private class SubmitTask extends AsyncTask<Void, Void, Boolean> {
		@Override
		protected Boolean doInBackground(Void... params) {
			if(addPlace(mName, mDescription, mAddress, mCategory, mTime, mLongitude, mLatitude)) {
				return true;
			}
			return false;
		}
		
		@Override
		protected void onPostExecute(Boolean result) {
			if(result) {
//				Intent i = new Intent(mContext, Main.class); 
//				i.putExtra("tab", 0);
//				i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//				mContext.startActivity(i);
				finish();
			} else {
				Toast.makeText(getApplicationContext(), "Error: Failed to submit to server", 
						Toast.LENGTH_SHORT).show();
			}
	    }
		
	}
	
	/**
	 * Guess operating time of the place given its category.
	 * 
	 * @param category Place category
	 * @return time value of the place, which is value of binary combination supper-dinner-lunch-breakfast
	 */
	private int guessTime(String category) {
		if(category.compareTo("Dessert") == 0 || category.compareTo("Restaurant") == 0 ||
				category.compareTo("Snacks") == 0) {
			vBreakfast.setChecked(false);
			vLunch.setChecked(true);
			vDinner.setChecked(true);
			vSupper.setChecked(false);
			return 6;
		} else if(category.compareTo("Cafe") == 0 || category.compareTo("Fastfood") == 0 ||
				category.compareTo("Foodcourt") == 0) {
			vBreakfast.setChecked(true);
			vLunch.setChecked(true);
			vDinner.setChecked(true);
			vSupper.setChecked(false);
			return 7;
		} else if(category.compareTo("Bar") == 0) {
			vBreakfast.setChecked(false);
			vLunch.setChecked(false);
			vDinner.setChecked(true);
			vSupper.setChecked(true);
			return 12;
		} else {
			vBreakfast.setChecked(true);
			vLunch.setChecked(true);
			vDinner.setChecked(true);
			vSupper.setChecked(true);
			return 15;
		}
	}
}
